import json
import threading
import tkinter as tk
from tkinter import ttk
from tkinter.messagebox import showwarning

from PIL import ImageTk,Image

from bases.atest_case import ATestCase
from cases.case_config import CasesConfig
from cases.case_fail_config import CaseFailConfig
from commons.common import dict_value_2_getkey
from commons.public_gobals import PublicGlobals
from windows import test_mode_index, testcase_refresh_loop, test_result_var, update_listview_GUI
from windows.red_dot import RedDot
from windows import test_time_bool, test_fail_id, report_data


# 用例运行的GUI逻辑控制(列表视图)
class CaseListView(ttk.Treeview):
    def __init__(self, master=None, **kwargs):
        super().__init__(master, **kwargs)

        # 创建 Style 对象并配置 Treeview 的字体
        style = ttk.Style()
        style.configure("Treeview", font=("微软雅黑", 12))  # 设置字体和大小
        style.configure("Treeview.Heading", font=("微软雅黑", 14, "bold"))  # 表头字体

        self.tag_configure("clear", background="#ffffff")  # 白色
        self.tag_configure("testing", background="yellow")  # 黄色
        self.tag_configure("pass", background="#00ff00")  # 绿色
        self.tag_configure("fail", background="#ff0000")  # 高亮色（粉红）
        self.tag_configure("done", background="#0000ff")  # 蓝色
        self.tag_configure("skip", background="#d3e3fd")  # 蓝色

        self.listRowsTitle= ['Name', 'Limit_L', 'Limit', 'Limit_H', 'Time','Result'][1:]
        self.config(columns=self.listRowsTitle,
                    # show='headings',     # 隐藏第一列，默认的树状图
                    )
        # 一级树节点
        self.init_tree = self.insert('',index=0,iid='init',text='init_初始化')
        self.scan_sn = self.insert('',index=1,iid='scan_sn',text='scan_sn')        # 输入SN
        self.pre_main_tree = self.insert('',index=2,iid='pre_main',text='pre_main') # pre_main
        self.main_tree = self.insert('', index=3,iid='main', text='main_主函数')
        self.post_main_tree = self.insert('',index=4, iid='post_main', text='post_main')  # post_main

        self.clear_tree = self.insert('', index=5,iid='clear', text='clear_复位')
        # 字典控制节点位置的一部字典用于锚定位置————锚定一级树节点的位置
        self.firstTreeRoot = {'init': self.init_tree,
                              # 'pre_main':self.pre_main_tree,
                            'main': self.main_tree,
                            'clear': self.clear_tree}
        self.keys = list(self.firstTreeRoot.keys())

        self.firstTreeRoot_2 = {
            'init': self.init_tree,
            'scan_sn':self.scan_sn,
            'pre_main':self.pre_main_tree,
            'main': self.main_tree,
            'post_main': self.post_main_tree,
            'clear': self.clear_tree
        }
        self.keys_2 = list(self.firstTreeRoot_2.keys())

        # 树形控件头列
        self.heading("#0", text="用例名称")  # 图标栏
        # 定义列
        # self.heading('Name', text='Name')   # 第0列的表头标签，实际显示值---名字已经确定是不需要的
        self.heading('Limit_L', text='下限')      # 需要
        self.heading('Limit', text='匹配方法')
        self.heading('Limit_H', text='上限')      # 需要
        self.heading('Time', text='时间')         # 需要
        self.heading('Result', text='Result')    # 需要

        # 样式配置
        self.column('#0', width=350)
        self.column('Limit_L', width=50, anchor='center')
        self.column('Limit', width=50, anchor='center')
        self.column('Limit_H', width=50, anchor='center')
        self.column('Time', width=50, anchor='center')
        self.column('Result', anchor='center')

        # 显示用例的窗口————加载用例到窗口
        for case_list, key in zip(CasesConfig.all_cases.get_all_cases_2(), self.keys_2):
            for i,case in enumerate(case_list):
                iid = f'{key}-' + str(i + 1).rjust(2, '0')
                self.insert(self.firstTreeRoot_2[key],"end",
                            text=f'[{iid}]'+case.title,
                            iid=iid,
                            values=(case.limit_L_run, case.match_method.value, case.limit_H_run)
                # 'Limit {==' + str(case.limit_H) + '}'
                            )

        # item_id = self.get_children()[1]  # 获取第2行的ID
        # self.set(item_id,column='Limit',value='ai')
        # self.item(item_id, tags='testing')

        # 添加右键菜单
        self.context_menu = tk.Menu(self, tearoff=0)  # 创建一个菜单
        self.context_menu.add_command(label="获取选中项下标(临时)", command=lambda: self.get_selected_item())
        self.context_menu.add_command(label="指定位置开始运行(待实现)", command=lambda: print("选择了选项2"))
        self.context_menu.add_command(label="添加断点", command=lambda: self.add_red_dot())
        self.context_menu.add_command(label="清除此断点", command=lambda: self.del_red_dot())
        self.bind('<3>', self.show_context_menu)

        # 断点的图像处理
        self.image = Image.open("./images/red_circle.png")
        self.photo = ImageTk.PhotoImage(self.image)
        # 保存断点位置的列表————初步为保存ID
        self.dot_iid_list = []


        # 逻辑变量————控制用例执行
        self.run_control_event = threading.Event()      # 控制其运行
        self.run_pause: bool = False        # 暂停标记
        self.run_end: bool = False          # 终止标记
        self.run_GOTO:bool = False
        self.last_run_index = 0
        self.main_test_result = []
        self.fail_id:list[str] = []                   # 测试项ID
        self.expand_all_items()

        update_listview_GUI.trace('r', lambda x,y,z:self.update_GUI_limit_args())

    def update_GUI_limit_args(self, key='main'):
        try:
            item_ids = self.get_children(self.firstTreeRoot_2[key])
            with open('./datafiles/main_limit_args.json', 'r', encoding='utf-8') as f:
                args_list = json.load(f)
                for item_id, args in zip(item_ids, args_list):
                    self.set(item_id, column='Limit_L', value=args['limit_L'])
                    self.set(item_id, column='Limit_H', value=args['limit_H'])
                    self.set(item_id, column='Limit', value=args['match_method'])
        except Exception as e:
            showwarning(message='更新GUI窗口失败')

    def expand_all_items(self):
        """展开Treeview中的所有项"""
        for item in self.get_children():
            self.item(item, open=True)
            # 递归展开所有子项
            self.expand_children(item)

    def expand_children(self, parent):
        """递归展开指定父项的所有子项"""
        for child in self.get_children(parent):
            self.item(child, open=True)
            self.expand_children(child)

    def run_end_OFF(self):
        self.run_end: bool = False          # 终止标记
    def run_end_ON(self):
        self.run_end = True
    def run_pause_OFF(self):
        self.run_pause = False
    def run_pause_ON(self):
        self.run_pause = True
    def run_GOTO_ON(self):
        self.run_GOTO = True
    def run_GOTO_OFF(self):
        self.run_GOTO = False
    # 对某一个用例的窗口展示设置标签，改变颜色
    def set_child_tag(self, index:int, tags:str, iid='main'):
        # print('index：', index)
        item_id = self.get_children(self.firstTreeRoot_2[iid])[index]
        # print('item_id:',item_id, type(item_id))
        self.item(item_id, tags=tags)

    # 运行状态设置，设置result的值————运行中的状态（重跑时要显示重跑的次数）
    def set_child_result_running(self, index, iid='main', value='测试中'):
        item_id = self.get_children(self.firstTreeRoot_2[iid])[index]
        self.set(item_id, column='Result', value=value)


    def set_child_result_re_running(self, *args):
        item_id = self.get_children(self.firstTreeRoot_2[self.key])[self.case_index]
        loop = testcase_refresh_loop.get()
        if loop == 0:
            pass
        else:
            self.set(item_id, column='Result', value='第'+str(testcase_refresh_loop.get())+'次重测中')
    # 设置result的值
    def set_child_result_value(self, index, case, iid='main'):
        item_id = self.get_children(self.firstTreeRoot_2[iid])[index]
        self.set(item_id, column='Result', value=case.result)       # 给出返回值
        self.set(item_id, column='Time', value=case.time)           # 给出运行时间

    def go_2_index(self, key_str:str='main'):
        index = self.keys_2.index(key_str)
        return index


    def post_update_child_limit_value(self, index, case:ATestCase, iid='main'):
        item_id = self.get_children(self.firstTreeRoot_2[iid])[index]
        # case.limit_args_2_global_value()        # 进行变量迭代
        self.set(item_id, column='Limit_H', value=case.limit_H_run)  # 给出返回值
        # self.set(item_id, column='Limit_H', value=case.limit_L_run)

    # 真正的新运行用例————一般都在子线程中运行---已启用
    def btn_run_event_2(self, _event):
        # print('运行逻辑2')
        tree_l = CasesConfig.all_cases.get_all_cases_2()
        # 展示重测次数
        testcase_refresh_loop.trace('w', self.set_child_result_re_running)


        tree_index = 0
        tree_length = len(tree_l)

        # 新增的两个逻辑变量
        flag = True             # 是否循环使用，终止时置为True--废弃
        pre_main_flag = False   # pre_main是否已经执行----废弃
        fail_flag = True        # 用于失败跳转----废弃

        # 外面也要循环控制，应该也用while循环，再控制下标进行跳转————0->1->2->3->4->5
        #         # while 列表 实质上是死循环                          main
        # while CasesConfig.all_cases.get_all_cases_2():
        while tree_index < tree_length:
        # for case_list, key in zip(CasesConfig.all_cases.get_all_cases_2(), self.keys_2):
            case_l:list[ATestCase] = tree_l[tree_index]
            self.case_index = 0
            case_length = len(case_l)
            self.key = self.keys_2[tree_index]
            # print(self.key, self.keys_2)
            # 每次测试的初始化____SN跑完之后
            if self.key == 'pre_main':
                test_result_var.set('Testing')
                test_time_bool.set(True)

            # 执行内部的内容
            while self.case_index < case_length:        # init_list,scan_sn,pre_main_list,main_list,post_main_list,clear_list
                # index = CasesConfig.all_cases.ptrCaseIndex
                # self.run_flag_RST()                 # 刷新终止标志
                if tree_index == 2 and pre_main_flag:
                    self.set_child_tag(self.case_index, 'done', self.key)  # 处于测试中,行颜色变换
                    # self.set_child_result_running(self.case_index)  # 处于测试中，添加result处文字
                    self.case_index += 1     # 已经跑过后，不跑的部分继续跳过
                    continue

                self.set_child_tag(self.case_index, 'testing', self.key)       # 处于测试中,行颜色变换
                self.set_child_result_running(self.case_index, self.key)       # 处于测试中，添加result处文字

                res = case_l[self.case_index].run(True)                    # 顺序执行每个用例
                self.post_update_child_limit_value(self.case_index, case_l[self.case_index], self.key)  # 界面变量的更新


                # print(res, res.value)                           # 输出res，通过结果
                # time.sleep(2)                                 # 模拟等待
                if res.value == 'PASS':                         # 设置颜色
                    self.set_child_tag(self.case_index, 'pass', self.key)
                elif res.value == 'FAIL':       # 测试结果为FAIL
                    self.set_child_tag(self.case_index, 'fail', self.key)
                    if self.key == 'main':
                        self.fail_id.append(self.key + '_' + str(self.case_index+1).rjust(2,'0'))
                        test_fail_id.set(self.fail_id[0])
                elif res.value == 'SKIP':
                    self.set_child_tag(self.case_index, 'skip', self.key)
                    self.set_child_result_running(self.case_index, self.key, 'SKIP')
                if self.key == 'main':
                    if self.case_index >= len(report_data):
                        report_data.append(case_l[self.case_index].result)  # 获取每次用例测试的返回值--添加
                    else:
                        report_data[self.case_index] = case_l[self.case_index].result   # 修正
                self.set_child_result_value(self.case_index, case_l[self.case_index], self.key)       # 用例跑完后，进行数据刷新。
                # CasesConfig.all_cases.index_add()
                self.case_index += 1
                # 输入SN时，scan_sn结束时，清空pre/main/post的数据
                if self.key == 'scan_sn' and self.case_index == case_length and res.value == 'PASS':
                    clear_t = threading.Thread(target=self.clear_sn_pre_main_post_case_tag, daemon=True)
                    clear_t.start()
                    test_fail_id.set(value='')      # 清除失败项ID
                    report_data.clear()             # 清除上次测试记录的测试数据————应该无关的
                    self.main_test_result.clear()   # 清除上次测试，得到的PASS/FAIL数据
                    self.fail_id.clear()  # 清除失败ID
                    # self.clear_sn_pre_main_post_case_tag()      # 清除颜色---比较耗时

                if self.key == 'main':
                    # MAIN中所有测试结果（PASS/FAIL）的保存
                    if self.case_index >= len(self.main_test_result):    # 长度小时，添加
                        self.main_test_result.append(res.value)
                    else:
                        self.main_test_result[self.case_index] = res.value  # 长度大时，修改

                    if self.case_index == case_length:  # 最后一步跑完
                        self.bool_temp = ('FAIL' in self.main_test_result)
                        if self.bool_temp:
                            test_result_var.set('FAIL')     # 显示本次测试结果FAIL
                        else:
                            test_result_var.set('PASS')     # 显示本次测试结果PASS


                ### 每条用例跑完后的后续逻辑处理0-init,1-scan_sn,2-pre_main,3-main,4-post-main,5-clear
                # 失败就结束——————————逻辑有问题，跳转逻辑需要分段处理
                if test_mode_index.get() == 0 and res.value == 'FAIL':
                    if tree_index in (self.go_2_index('init'), self.go_2_index('scan_sn')):       # 逻辑已实现
                        tree_index = self.go_2_index('clear')       # 当树节点下标为init时，直接跳转到clear终止一切
                        self.run_end_ON()                           # 打开终止标志，保证其正常结束
                    elif tree_index in (self.go_2_index('pre_main'), self.go_2_index('main')):
                        tree_index = self.go_2_index('post_main')
                        self.run_GOTO_ON()      # 打开跳转标志
                        test_result_var.set(value='FAIL')

                # 失败就暂停——————————可能也需要重改逻辑
                elif test_mode_index.get() == 1 and res.value == 'FAIL':
                    self.run_pause_ON()     # 开启暂停标志
                elif test_mode_index.get() == 2:
                    pass  # 失败就继续跑，忽略不计,不做处理
                # 断点暂停功能————需要重写
                self.dot_pause(self.key, self.case_index)
                # 暂停标志----与断点关联
                if self.run_pause:
                    _event.wait()       # 暂停----停止这个线程
                if self.run_end:                            # 终止标志——跳出循环
                    tree_index = self.go_2_index('clear')    # 直接去跑clear节点部分----终止部分
                    # print(tree_index)
                    flag = False
                    break
                if self.run_GOTO:       # 如果需要跳转逻辑，跳出内循环
                    break
            if self.key == 'post_main': # pre_main跑完，终止计时
                test_time_bool.set(False)
                if self.fail_id:
                    CaseFailConfig.sorted_top_fail_GUI_update(self.fail_id[0])    # 直接进行fail_top刷新

            if self.run_end:
                self.run_end_OFF()      # 终止标志逻辑已触发，标志关闭
                self.run_pause_OFF()    # 触发终止行为时，暂停程序应该直接开始运行终止的程序.
                continue
            if self.run_GOTO:
                self.run_GOTO_OFF()     # 关闭跳转标志
                continue        # 重置内循环
            # pre_main跑完一次后，就不再跑了(似乎不需要这个逻辑)
            # if tree_index == 2:
            #     pre_main_flag = True
            tree_index += 1
            # 循环标志位写法
            if tree_index == 5 and flag:  # 进行跳转--循环
                tree_index = 1
                fail_flag = True
            if tree_index == tree_length:   # 最后一步跑完，就结束
                # print('总长度：', tree_length)
                break
        # print('用例运行结束')


    # 运行用例——开始测试
    def run_event(self):
        # 清除暂停与终止的标志位
        self.clear_pause_end_flag()
        # 清除全部用例的颜色
        self.clear_case_tag()
        # t = threading.Thread(target=self.btn_run_event,daemon=True, args=(self.run_control_event,))
        t = threading.Thread(target=self.btn_run_event_2, daemon=True, args=(self.run_control_event,))
        t.start()       # 开启子线程
        test_result_var.set(value='Testing')

    # 断点判断暂停功能--已实现
    def dot_pause(self, key, index):
        print('self.case_index:', self.case_index)
        dot_iid = key + '-' + str(index).rjust(2,'0')
        print(dot_iid, self.dot_iid_list)
        if dot_iid in self.dot_iid_list:        # 当前的dot
            self.run_pause = True

    def show_context_menu(self, event):
        # 特别的显示
        if self.selection():  #
            self.context_menu.post(event.x_root, event.y_root)


    def get_selected_item(self):
        selected_item = self.selection()[0]
        print(self.selection(), type(self.selection()))  # 获取选中项,元组类型输出——列表视图按住Ctrl键可以多选
        values = self.item(selected_item)
        print(values)
        print("选中项的ID：", selected_item)
        print("选中项的值：", values["text"])
        print("选中项的标签：", values["tags"])
        print("选中项的图标：", values["image"])


    # 重新输入SN时，清除main的颜色
    def clear_sn_pre_main_post_case_tag(self):
        for l,key in zip(CasesConfig.all_cases.get_all_cases_2()[2:5],self.keys_2[2:5]):
            for i in range(len(l)):
                # self.set_child_tag(i, 'clear', key)
                item_id = self.get_children(self.firstTreeRoot_2[key])[i]
                # print('item_id:',item_id, type(item_id))
                self.item(item_id, tags='clear')
                self.set(item_id, column='Result', value='')        #
                self.set(item_id, column='Time', value='')

    # 清除所有用例的颜色
    def clear_case_tag(self):
        for case_list, key in zip(CasesConfig.all_cases.get_all_cases(), self.keys):
            # while CasesConfig.all_cases.test_case_list:
            while case_list:
                index = CasesConfig.all_cases.ptrCaseIndex
                self.set_child_tag(index, 'clear', key)
                CasesConfig.all_cases.ptrCaseIndex += 1
                if CasesConfig.all_cases.ptrCaseIndex == len(case_list):
                    break
            CasesConfig.all_cases.ptrCaseIndex = 0
    # 清除标志位----暂停和终止
    def clear_pause_end_flag(self):
        self.run_pause = False
        self.run_end = False


    # 程序停止
    def btn_pause_event(self):
        self.run_pause = True  # 程序停止

    # 程序再次启动__正常运行
    def btn_continue_event(self):
        self.run_pause = False
        self.run_control_event.set()
        self.run_control_event.clear()

    # 程序再次启动__单_步_运行
    def btn_single_run_event(self):
        # global global_pause
        # global_pause = False
        self.run_control_event.set()
        self.run_control_event.clear()

    # 终止程序运行
    def btn_end_event(self):
        self.run_end = True

    def add_red_dot(self):
        print('添加断点（GUI已实现）')
        # dot = RedDot(self)
        # dot.place(x=100, y=100)
        selected_item = self.selection()[0]
        print(self.selection(), type(self.selection()))  # 获取选中项,元组类型输出——列表视图按住Ctrl键可以多选
        values = self.item(selected_item, image=self.photo)     # 设置断点
        # 添加进入列表
        self.dot_iid_list.append(selected_item)
        print(self.dot_iid_list)
    def del_red_dot(self):
        print('清除此断点（GUI已实现）')
        # dot = RedDot(self)
        # dot.place(x=100, y=100)
        selected_item = self.selection()[0]
        # print(self.selection(), type(self.selection()))  # 获取选中项,元组类型输出——列表视图按住Ctrl键可以多选
        values = self.item(selected_item, image='')     # 设置断点
        # 从列表中删除
        if selected_item in self.dot_iid_list:
            self.dot_iid_list.remove(selected_item)
            print(self.dot_iid_list)


if __name__ == '__main__':
    CasesConfig.case_init()     # 用例配置初始化
    root = tk.Tk()
    listView = CaseListView(root)
    listView.pack()
    btn = tk.Button(text='运行', command=listView.run_event)
    btn.pack()
    root.mainloop()
